#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <alsa/asoundlib.h>
#include <sys/stat.h> // For stat() and S_ISREG()

// 播放原始音频数据到ALSA设备
int play_raw_audio(const char *device_name, FILE *audio_file, long file_size);

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <raw_audio_file>\n", argv[0]);
        return EXIT_FAILURE;
    }

    const char *raw_filename = argv[1];
    const char *alsa_device = "plughw:6,0"; // 指定USB声卡设备，请根据实际情况修改！

    // 获取文件大小，以便知道要读取多少数据
    struct stat st;
    if (stat(raw_filename, &st) == -1) {
        perror("Error getting file size");
        return EXIT_FAILURE;
    }
    long file_size = st.st_size;

    FILE *raw_fp = fopen(raw_filename, "rb");
    if (!raw_fp) {
        perror("Error opening raw audio file");
        return EXIT_FAILURE;
    }

    printf("Attempting to play raw audio from %s to ALSA device: %s\n", raw_filename, alsa_device);
    printf("Assuming format: Sample Rate = 44100 Hz, Channels = 2, Bits per Sample = 16 (S16_LE)\n");
    printf("File size: %ld bytes\n", file_size);


    if (play_raw_audio(alsa_device, raw_fp, file_size) != 0) {
        fprintf(stderr, "Error playing raw audio.\n");
        fclose(raw_fp);
        return EXIT_FAILURE;
    }

    printf("Raw audio playback finished.\n");

    fclose(raw_fp);
    return EXIT_SUCCESS;
}

// 播放原始音频数据到ALSA设备
int play_raw_audio(const char *device_name, FILE *audio_file, long file_size) {
    snd_pcm_t *handle;
    snd_pcm_hw_params_t *params;
    int err;

    // 直接指定所需的参数，因为我们不从文件头解析
    unsigned int sample_rate = 44100;
    int num_channels = 2;
    int bits_per_sample = 16;
    snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE; // 16位有符号小端

    // 打开PCM设备
    if ((err = snd_pcm_open(&handle, device_name, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
        fprintf(stderr, "Cannot open audio device %s: %s\n", device_name, snd_strerror(err));
        return -1;
    }

    // 分配硬件参数结构体
    snd_pcm_hw_params_alloca(&params);

    // 填充默认值
    if ((err = snd_pcm_hw_params_any(handle, params)) < 0) {
        fprintf(stderr, "Cannot initialize hardware parameter structure: %s\n", snd_strerror(err));
        goto cleanup;
    }

    // 设置访问模式为交错模式 (Interleaved)
    if ((err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
        fprintf(stderr, "Cannot set access type: %s\n", snd_strerror(err));
        goto cleanup;
    }

    // 设置采样格式
    if ((err = snd_pcm_hw_params_set_format(handle, params, format)) < 0) {
        fprintf(stderr, "Cannot set sample format: %s\n", snd_strerror(err));
        goto cleanup;
    }

    // 设置声道数
    if ((err = snd_pcm_hw_params_set_channels(handle, params, num_channels)) < 0) {
        fprintf(stderr, "Cannot set channel count: %s\n", snd_strerror(err));
        goto cleanup;
    }

    // 设置采样率
    unsigned int actual_rate = sample_rate;
    if ((err = snd_pcm_hw_params_set_rate_near(handle, params, &actual_rate, 0)) < 0) {
        fprintf(stderr, "Cannot set sample rate: %s\n", snd_strerror(err));
        goto cleanup;
    }
    printf("Actual sample rate: %d\n", actual_rate);
    if (actual_rate != sample_rate) {
        fprintf(stderr, "Warning: Rate %u Hz not supported by hardware, using %u Hz instead.\n", sample_rate, actual_rate);
    }

    // 设置缓冲区大小 (period size)
    snd_pcm_uframes_t frames;
    frames = 10240; // 每次写入的帧数，可以根据需要调整
    if ((err = snd_pcm_hw_params_set_period_size_near(handle, params, &frames, 0)) < 0) {
        fprintf(stderr, "Cannot set period size: %s\n", snd_strerror(err));
        goto cleanup;
    }
    printf("realy period size: %lu\n", frames);
    // 设置缓冲区总大小
    snd_pcm_uframes_t buffer_size = frames * 64; // 例如，缓冲区是period的4倍
    if ((err = snd_pcm_hw_params_set_buffer_size_near(handle, params, &buffer_size)) < 0) {
        fprintf(stderr, "Cannot set buffer size: %s\n", snd_strerror(err));
        goto cleanup;
    }
    printf("realy buffer size: %lu\n", buffer_size);

    // 将参数写入驱动
    if ((err = snd_pcm_hw_params(handle, params)) < 0) {
        fprintf(stderr, "Cannot set parameters: %s\n", snd_strerror(err));
        goto cleanup;
    }

    // 准备PCM设备
    if ((err = snd_pcm_prepare(handle)) < 0) {
        fprintf(stderr, "Cannot prepare audio interface for use: %s\n", snd_strerror(err));
        goto cleanup;
    }

    // 读取音频数据并写入声卡
    char *buffer;
    snd_pcm_uframes_t period_size;
    snd_pcm_hw_params_get_period_size(params, &period_size, 0);
    printf("snd_pcm_hw_params_get_period_size size: %lu\n", period_size);
    size_t bytes_per_frame = (size_t)bits_per_sample / 8 * num_channels;
    size_t buffer_bytes = period_size * bytes_per_frame;

    buffer = (char *)malloc(buffer_bytes);
    if (!buffer) {
        fprintf(stderr, "Failed to allocate audio buffer.\n");
        goto cleanup;
    }

    long remaining_bytes = file_size; // 使用整个文件大小
    while (remaining_bytes > 0) {
        size_t bytes_to_read = (remaining_bytes < buffer_bytes) ? remaining_bytes : buffer_bytes;
        size_t bytes_read = fread(buffer, 1, bytes_to_read, audio_file);
        if (bytes_read == 0) {
            fprintf(stderr, "End of raw audio file or read error.\n");
            break;
        }

        snd_pcm_uframes_t frames_to_write = bytes_read / bytes_per_frame;
        snd_pcm_sframes_t frames_written = snd_pcm_writei(handle, buffer, frames_to_write);

        if (frames_written < 0) {
            if (frames_written == -EPIPE) { // XRUN occurred (underrun or overrun)
                fprintf(stderr, "ALSA XRUN, attempting to recover...\n");
                snd_pcm_prepare(handle);
            } else {
                fprintf(stderr, "Error writing to audio interface: %s\n", snd_strerror(frames_written));
                goto cleanup_buffer;
            }
        } else if (frames_written != frames_to_write) {
            fprintf(stderr, "Short write to ALSA: expected %lu frames, wrote %ld\n", frames_to_write, frames_written);
        }
        remaining_bytes -= (frames_written * bytes_per_frame);
    }

    // 等待所有缓冲数据播放完毕
    snd_pcm_drain(handle);

cleanup_buffer:
    free(buffer);
cleanup:
    snd_pcm_close(handle);
    return err < 0 ? -1 : 0;
}


//aarch64-none-linux-gnu-gcc audio_playWav.c -o playWav.app  --sysroot=/path/to/buildroot/sysroot     -lavformat -lavcodec -lavutil -lswscale -lm  -lasound  -lsndfile